/*
 * Copyright (c) 2009, Hamish Morgan
 * All rights reserver.
 */
package locusts.common.behaviours;

import java.util.ArrayList;
import java.util.List;

import locusts.common.entities.Flock;
import locusts.common.entities.Vehicle;

/**
 * AbstractFlockBehaviour objects are behaviours that require standard
 * boids style parameters: neighbourhood radius, angle, and a list of
 * vehicles that are in the flock.
 *
 * @author Hamish Morgan
 */
public abstract class AbstractFlockBehaviour extends AbstractBehaviour {
    
    private static final double DEFAULT_MIN_DISTANCE = 0d;
    private static final double DEFAULT_MAX_DISTANCE = 100;
    private static final double DEFAULT_MAX_ANGLE = 0.7 * Math.PI;
    
    private Flock flock;
    private double minDistance = DEFAULT_MIN_DISTANCE;
    private double maxDistance = DEFAULT_MAX_DISTANCE;
    private double maxAngle = DEFAULT_MAX_ANGLE;
    
    protected AbstractFlockBehaviour(double w, Flock flock,
            double minDist, double maxDist, double maxAng) {
        super(w);
        setFlock(flock);
        setMaxAngle(maxAng);
        setMinDistance(minDist);
        setMaxDistance(maxDist);
    }
    
    public void setMaxDistance(double maxDistance) {
        if (Double.isNaN(maxDistance) || Double.isInfinite(maxDistance)
                || maxDistance < 0)
            throw new IllegalArgumentException();
        if (minDistance >= maxDistance)
            throw new IllegalArgumentException();
        this.maxDistance = maxDistance;
    }
    
    public void setMinDistance(double minDistance) {
        if (Double.isNaN(minDistance) || Double.isInfinite(minDistance)
                || minDistance < 0)
            throw new IllegalArgumentException();
        if (minDistance >= maxDistance)
            throw new IllegalArgumentException();
        this.minDistance = minDistance;
    }
    
    public void setMaxAngle(double maxAngle) {
        if (Double.isNaN(maxAngle) || Double.isInfinite(maxAngle))
            throw new IllegalArgumentException();
        this.maxAngle = maxAngle;
    }
    
    public void setFlock(Flock flock) {
        if (flock == null)
            throw new IllegalArgumentException();
        this.flock = flock;
    }
    
    protected List<Vehicle> getNeighbors(Vehicle boid) {
        List<Vehicle> neighbors = new ArrayList<Vehicle>();
        
        for (Vehicle other : flock.getBoids()) {
            if (isNeighbor(boid, other)) 
                neighbors.add(other);
        }
        return neighbors;
    }
    
    // TODO: wrong! This doesn't take into account a's heading
    public boolean isNeighbor(Vehicle a, Vehicle b) {
        if (a == null || b == null)
            throw new IllegalArgumentException();
        // this is not a neighbor of itself
        if (b == a)
            return false;
        
        // check it is within the minimum distance
        final double dx = b.position.x - a.position.x;
        if (dx > maxDistance)
            return false;
        final double dy = b.position.y - a.position.y;
        if (dy > maxDistance)
            return false;
        
        double lenSquared = dx * dx + dy * dy;
        if (lenSquared > maxDistance * maxDistance)
            return false;
        if (lenSquared < minDistance * minDistance)
            return false;
        
        double bearing = a.bearingTo(b);

        
        if (bearing > maxAngle)
            return false;
        
        return true;
    }
}